/*
 * Copyright (c) Jack魏 2023 - 2023, All Rights Reserved.
 */

package cn.jack1996.yiyi.auth;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.ExceptionTranslationFilter;

/**
 * Security自定义配置类 官网教程: https://docs.spring.io/spring-security/reference/servlet/configuration/java.html
 * 注意：在5.7.0之后废弃了WebSecurityConfigurerAdapter
 *
 * @author Jack魏
 * @since 2023/1/5 22:55
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Autowired
    private CustomUserDetailService userDetailService;

    @Autowired
    private AuthenticationFilter authenticationFilter;

    /**
     * 密码加密方式，如果没有这个会报错：
     *
     * @return 用户加密后密码
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        // 因为数据库采用明文存储密码所以采用不加密方式，不然会警告：There is no PasswordEncoder mapped for the id "null"
        return NoOpPasswordEncoder.getInstance();
    }

    /**
     * 过滤链
     *
     * @param httpSecurity 请求
     * @return 请求构建
     * @throws Exception 异常
     */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        // 关闭跨域请求伪造
        httpSecurity.csrf().disable()
            // 关闭登录框
            .formLogin().disable()
            // 添加自定义的过滤器链
            .addFilterAfter(authenticationFilter, ExceptionTranslationFilter.class)
            // 不创建session，不用HttpSession获取SecurityContext
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            // 这里的异常不受全异常处理类控制，需要这里输出到页面提示
            .and().exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
                response.setContentType("text/plain;charset=UTF-8");
                response.getWriter().write(authException.getMessage());
            }).accessDeniedHandler((request, response, accessDeniedException) -> {
                response.setContentType("text/plain;charset=UTF-8");
                response.getWriter().write(accessDeniedException.getMessage());
            });
        return httpSecurity.build();
    }

    /**
     * 认证管理
     *
     * @return 认证管理配置
     */
    @Bean
    public AuthenticationManager authenticationManager() {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        // 调用loadUserByUsername方法获取用户信息
        daoAuthenticationProvider.setUserDetailsService(userDetailService);
        // 通过自定义加密方式去验证密码是否正确
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        return new ProviderManager(daoAuthenticationProvider);
    }
}
